-
Notifications
You must be signed in to change notification settings - Fork 30
Implement tensor.isin
#2098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Implement tensor.isin
#2098
Conversation
View rendered docs @ https://intelpython.github.io/dpctl/pulls/2098/index.html |
Array API standard conformance tests for dpctl=0.21.0dev0=py310h93fe807_8 ran successfully. |
isin leverages kernel very similar to searchsorted, but after the search, the position is checked, and if the position is equal to the number of elements in the searched array, existence is considered false
1805102
to
5355fb8
Compare
Array API standard conformance tests for dpctl=0.21.0dev0=py310h93fe807_10 ran successfully. |
dpctl/tensor/_set_functions.py
Outdated
|
||
dep_evs = _manager.submitted_events | ||
ht_ev, s_ev = _isin( | ||
needles=x1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems the only case when the strided implementation (which assumes slower) will be used when x1
array is not contiguous (we sort test_elements
array and no out
keyword in isin
function).
Would it make sense to flatten input array x
and to pass order
keyword there?
But, it makes sense also to keep strided implementation of _isin
in case when it might be helpful in implementation of other set functions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can experiment and see values of flattening vs. not flattening
but in general, this implementation is going to be changed quite a bit soon, I have some local changes waiting
permit scalar input for second argument, address some review comments, add docstring
@antonwolfy tests still need to be added, but |
Array API standard conformance tests for dpctl=0.21.0dev0=py310h93fe807_17 ran successfully. |
@@ -112,6 +112,7 @@ set(_reduction_sources | |||
${CMAKE_CURRENT_SOURCE_DIR}/libtensor/source/reductions/sum.cpp | |||
) | |||
set(_sorting_sources | |||
${CMAKE_CURRENT_SOURCE_DIR}/libtensor/source/sorting/isin.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem relating to sorting routine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it uses common utilities with searchsorted
(i.e., from rich_comparisons.hpp
) which is why it lives there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the code from rich_comparisons
gets factored out, I can go ahead and move it elsewhere, I guess to _tensor_impl
for now
input array. | ||
test_elements (Union[usm_ndarray, bool, int, float, complex]): | ||
elements against which to test each value of `x`. | ||
Default: `None`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can't be None
I assume
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, copy-paste error
# copy into C-contiguous memory, because the array will be flattened | ||
test_buf = dpt.empty_like(test_elements, dt, order="C") | ||
dep_evs = _manager.submitted_events | ||
ht_ev, ev = _copy_usm_ndarray_into_usm_ndarray( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This copy and the one at line 698 can be done independently if any.
x_buf = x | ||
|
||
if not isinstance(test_elements, dpt.usm_ndarray): | ||
test_buf = dpt.asarray(test_elements, dtype=dt, sycl_queue=exec_q) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to pass usm_type
keyword?
test_buf = dpt.reshape(test_buf, -1) | ||
test_buf = dpt.sort(test_buf) | ||
|
||
dst = _empty_like_orderK(x_buf, dpt.bool, usm_type=res_usm_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to keep the same order like used by x
? Wouldn't it be better to return C-contig array?
test_dt = _get_dtype(test_elements, sycl_dev) | ||
if not _validate_dtype(test_dt): | ||
raise ValueError("`test_elements` has unsupported dtype") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to handle the corner case separately at some point?
if isinstance(test_elements, dpt.usm_ndarray) and test_elements.size == 0:
if invert:
return dpt.ones_like(ar1, dtype=bool, usm_type=res_usm_type)
else:
return dpt.zeros_like(ar1, dtype=bool, usm_type=res_usm_type)
fnT get() const | ||
{ | ||
using dpctl::tensor::kernels::isin_contig_impl; | ||
using Compare = typename AscendingSorter<argTy>::type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to have Compare
templated here? Is there any use case possible when another one will be required to be used by isin
kernel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not strictly necessary, but was done to reduce code duplication—these sorters are defined in tensor/source
.
I can look at the normal sort implementation to refresh myself on what was done there, but if that isn't sufficient, it may be preferable to template and pass the sorter here as opposed to duplicating in isin.hpp
|
||
void operator()(sycl::id<1> id) const | ||
{ | ||
const Compare comp{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const Compare comp{}; | |
static constexpr Compare comp{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point!
test_buf = dpt.asarray(test_elements, dtype=dt, sycl_queue=exec_q) | ||
elif test_dt != dt: | ||
# copy into C-contiguous memory, because the array will be flattened | ||
test_buf = dpt.empty_like(test_elements, dt, order="C") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test_buf = dpt.empty_like(test_elements, dt, order="C") | |
test_buf = dpt.empty_like(test_elements, dtype=dt, order="C") |
This PR proposes an implementation for
isin
, a function likely coming to a future array API specification, which leverages a similar kernel to the implementation ofsearchsorted
This implementation uses the
searchsorted
kernel to check if the value has a position in the array. If that position is the number of elements in the array, it is not a member. Otherwise, ifarr[pos] == val
for some arrayarr
being searched for valueval
, thenval
is a member.